﻿#include "VulkanTextureCubes.h"
#include "Render/PreRender/PreRenderTextureCube.h"
#include "Render/Texture/WriteInTextureCube.h"
#include "Render/Texture/VulkanTextureFormat.h"
#include "Render/Texture/VulkanTextureCube.h"
#include "Render/VulkanContext.h"

#include "Texture/Texture2D.h"

void CVulkanTextureCubes::UpdateTextures(const TVector<std::shared_ptr<CPreRenderTextureCube>>& AllTextureData)
{
    auto OldTextureCubes=TextureCubes;
    TextureCubes.Clear();

    TVector<TThreadSafeSharedPtr<CWriteInTextureCube>> NeedUpdateTextureCubes;
    for(auto &PreRenderTexture:AllTextureData)
    {
        auto TextureData=PreRenderTexture->WriteInData;
        if(OldTextureCubes.Contains(TextureData))
        {
            TextureCubes.Add(TextureData, OldTextureCubes[TextureData]);
            if(TextureData->IsDirty())
            {
                NeedUpdateTextureCubes.Add(TextureData);
            }
            continue;
        }
        else
        {
            auto VulkanTextureCube=std::make_shared<CVulkanTextureCube>();
            TextureCubes.Add(TextureData, VulkanTextureCube);
            NeedUpdateTextureCubes.Add(TextureData);
        }
    }

    OldTextureCubes.Clear();

    for(auto &TextureData:NeedUpdateTextureCubes)
    {
        auto VulkanTextureCube=TextureCubes[TextureData];
        std::lock_guard<std::mutex> LockGuard(TextureData->Mutex);
        TextureData->bDirty=false;

        vk::Format Format=ToVulkanTextureFormat(TextureData->Format);
        assert(Format!=vk::Format::eUndefined);

        //创建cube texture
        vk::ImageCreateInfo ImageCreateInfo;
        ImageCreateInfo.flags=vk::ImageCreateFlagBits::eCubeCompatible;
        ImageCreateInfo.imageType=vk::ImageType::e2D;
        ImageCreateInfo.format=Format;
        ImageCreateInfo.extent.width=TextureData->Width;
        ImageCreateInfo.extent.height=TextureData->Height;
        ImageCreateInfo.extent.depth=1;
        ImageCreateInfo.mipLevels=1;
        ImageCreateInfo.mipLevels=1;
        ImageCreateInfo.arrayLayers=6;
        ImageCreateInfo.samples=vk::SampleCountFlagBits::e1;
        ImageCreateInfo.tiling=vk::ImageTiling::eOptimal;
        ImageCreateInfo.usage=vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
        ImageCreateInfo.sharingMode=vk::SharingMode::eExclusive;
        ImageCreateInfo.initialLayout=vk::ImageLayout::ePreinitialized;

        //清理旧的资源
        if(VulkanTextureCube->Image)
        {
            CVulkanContext::Get()->GetRecycleObjects()->Images.Add(VulkanTextureCube->Image);
            CVulkanContext::Get()->GetRecycleObjects()->ImageViews.Add(VulkanTextureCube->ImageView);
            CVulkanContext::Get()->GetRecycleObjects()->Memories.Add(VulkanTextureCube->ImageMemory);
        
            VulkanTextureCube->Image=nullptr;
            VulkanTextureCube->ImageView=nullptr;
            VulkanTextureCube->ImageMemory=nullptr;
        }

        VulkanTextureCube->Image=CVulkanContext::Get()->GetDevice().createImage(ImageCreateInfo);

        vk::MemoryRequirements MemoryRequirements=CVulkanContext::Get()->GetDevice().getImageMemoryRequirements(VulkanTextureCube->Image);

        vk::MemoryAllocateInfo MemoryAllocateInfo=vk::MemoryAllocateInfo()
            .setAllocationSize(MemoryRequirements.size)
            .setMemoryTypeIndex(CVulkanContext::Get()->GetMemoryTypeIndex(MemoryRequirements, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent));
        VulkanTextureCube->ImageMemory=CVulkanContext::Get()->GetDevice().allocateMemory(MemoryAllocateInfo);
        CVulkanContext::Get()->GetDevice().bindImageMemory(VulkanTextureCube->Image,VulkanTextureCube->ImageMemory,0);

        vk::ImageViewCreateInfo ImageViewCreateInfo;
        ImageViewCreateInfo.image=VulkanTextureCube->Image;
        ImageViewCreateInfo.viewType=vk::ImageViewType::eCube;
        ImageViewCreateInfo.format=ImageCreateInfo.format;
        ImageViewCreateInfo.subresourceRange.aspectMask=vk::ImageAspectFlagBits::eColor;
        ImageViewCreateInfo.subresourceRange.baseMipLevel=0;
        ImageViewCreateInfo.subresourceRange.levelCount=1;
        ImageViewCreateInfo.subresourceRange.baseArrayLayer=0;
        ImageViewCreateInfo.subresourceRange.layerCount=6;

        VulkanTextureCube->ImageView=CVulkanContext::Get()->GetDevice().createImageView(ImageViewCreateInfo);

        VulkanTextureCube->ImageSize.width=TextureData->Width;
        VulkanTextureCube->ImageSize.height=TextureData->Height;


        //将图片布局转换为transfer dst optimal
        vk::CommandBuffer ConvertImageLayoutCommandBuffer=CVulkanContext::Get()->GetConvertImageLayoutCommandBuffer();
        
        {
            vk::CommandBufferBeginInfo CommandBufferBeginInfo=vk::CommandBufferBeginInfo()
                .setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);

            ConvertImageLayoutCommandBuffer.begin(CommandBufferBeginInfo);
        }

        vk::ImageMemoryBarrier ImageMemoryBarrier=vk::ImageMemoryBarrier()
            .setSrcAccessMask(vk::AccessFlagBits::eNone)
            .setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
            .setOldLayout(vk::ImageLayout::ePreinitialized)
            .setNewLayout(vk::ImageLayout::eTransferDstOptimal)
            .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
            .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
            .setImage(VulkanTextureCube->Image)
            .setSubresourceRange(vk::ImageSubresourceRange()
                .setAspectMask(vk::ImageAspectFlagBits::eColor)
                .setBaseMipLevel(0)
                .setLevelCount(1)
                .setBaseArrayLayer(0)
                .setLayerCount(6));

        ConvertImageLayoutCommandBuffer.pipelineBarrier(
            vk::PipelineStageFlagBits::eTopOfPipe
            , vk::PipelineStageFlagBits::eTransfer
            , vk::DependencyFlags()
            , nullptr
            , nullptr
            , ImageMemoryBarrier);

        uint32_t FaceSize=TextureData->Pitch*TextureData->Height;
        uint32_t TotalSize=FaceSize*6;
        uint32_t Offset=0;

        //创建staging buffer
        vk::Buffer StagingBuffer;
        vk::DeviceMemory StagingBufferMemory;

        vk::BufferCreateInfo BufferCreateInfo;
        BufferCreateInfo.size=TotalSize;
        BufferCreateInfo.usage=vk::BufferUsageFlagBits::eTransferSrc;
        BufferCreateInfo.sharingMode=vk::SharingMode::eExclusive;

        StagingBuffer=CVulkanContext::Get()->GetDevice().createBuffer(BufferCreateInfo);

        vk::MemoryRequirements StagingBufferMemoryRequirements=CVulkanContext::Get()->GetDevice().getBufferMemoryRequirements(StagingBuffer);

        vk::MemoryAllocateInfo StagingBufferMemoryAllocateInfo;
        StagingBufferMemoryAllocateInfo.allocationSize=StagingBufferMemoryRequirements.size;
        StagingBufferMemoryAllocateInfo.memoryTypeIndex=CVulkanContext::Get()->GetMemoryTypeIndex(StagingBufferMemoryRequirements, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);

        StagingBufferMemory=CVulkanContext::Get()->GetDevice().allocateMemory(StagingBufferMemoryAllocateInfo);

        CVulkanContext::Get()->GetDevice().bindBufferMemory(StagingBuffer,StagingBufferMemory,0);

        void* StagingBufferData=CVulkanContext::Get()->GetDevice().mapMemory(StagingBufferMemory,0,VK_WHOLE_SIZE);
        
        //填充图像数据， 
        //需要按照opengl(不是vulkan)的NDC坐标系的 +x(右) -x(左) +y(上) -y(下) +z(后) -z(前)的顺序填充数据
        //然后在shader中将引擎坐标系转换为opengl坐标系以正确采样
        //用于天空球时图片存在左右相反的问题，所以需要在导入时将图片左右翻转
        RTexture2D::HorizontalFlip(TextureData->Front,TextureData->Width,TextureData->Height,TextureData->Pitch,TextureData->Format);
        RTexture2D::HorizontalFlip(TextureData->Back,TextureData->Width,TextureData->Height,TextureData->Pitch,TextureData->Format);
        RTexture2D::HorizontalFlip(TextureData->Left,TextureData->Width,TextureData->Height,TextureData->Pitch,TextureData->Format);
        RTexture2D::HorizontalFlip(TextureData->Right,TextureData->Width,TextureData->Height,TextureData->Pitch,TextureData->Format);
        
        memcpy(StagingBufferData,TextureData->Right.Data(),FaceSize);
        memcpy((uint8_t*)StagingBufferData+FaceSize,TextureData->Left.Data(),FaceSize);
        memcpy((uint8_t*)StagingBufferData+FaceSize*2,TextureData->Top.Data(),FaceSize);
        memcpy((uint8_t*)StagingBufferData+FaceSize*3,TextureData->Bottom.Data(),FaceSize);
        memcpy((uint8_t*)StagingBufferData+FaceSize*4,TextureData->Back.Data(),FaceSize);
        memcpy((uint8_t*)StagingBufferData+FaceSize*5,TextureData->Front.Data(),FaceSize);
        CVulkanContext::Get()->GetDevice().unmapMemory(StagingBufferMemory);

        //拷贝数据到ImageMemory

        vk::ImageSubresourceLayers ImageSubresourceLayers;
        ImageSubresourceLayers.aspectMask=vk::ImageAspectFlagBits::eColor;
        ImageSubresourceLayers.mipLevel=0;
        ImageSubresourceLayers.baseArrayLayer=0;
        ImageSubresourceLayers.layerCount=6;

        vk::BufferImageCopy BufferImageCopy;
        BufferImageCopy.bufferOffset=0;
        BufferImageCopy.bufferRowLength=0;
        BufferImageCopy.bufferImageHeight=0;
        BufferImageCopy.imageSubresource=ImageSubresourceLayers;
        BufferImageCopy.imageOffset=vk::Offset3D(0,0,0);
        BufferImageCopy.imageExtent=vk::Extent3D(TextureData->Width,TextureData->Height,1);

        ConvertImageLayoutCommandBuffer.copyBufferToImage(StagingBuffer,VulkanTextureCube->Image,vk::ImageLayout::eTransferDstOptimal,1,&BufferImageCopy);


        //当复制完后，再将图片布局转换为shader read only
        vk::ImageMemoryBarrier ImageMemoryBarrier2=vk::ImageMemoryBarrier()
            .setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
            .setDstAccessMask(vk::AccessFlagBits::eShaderRead)
            .setOldLayout(vk::ImageLayout::eTransferDstOptimal)
            .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
            .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
            .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
            .setImage(VulkanTextureCube->Image)
            .setSubresourceRange(vk::ImageSubresourceRange()
                .setAspectMask(vk::ImageAspectFlagBits::eColor)
                .setBaseMipLevel(0)
                .setLevelCount(1)
                .setBaseArrayLayer(0)
                .setLayerCount(6));

        ConvertImageLayoutCommandBuffer.pipelineBarrier(
            vk::PipelineStageFlagBits::eTransfer
            , vk::PipelineStageFlagBits::eFragmentShader
            , vk::DependencyFlags()
            , nullptr
            , nullptr
            , ImageMemoryBarrier2);


        ConvertImageLayoutCommandBuffer.end();

        {
            vk::SubmitInfo SubmitInfo=vk::SubmitInfo()
                .setCommandBufferCount(1)
                .setPCommandBuffers(&ConvertImageLayoutCommandBuffer);

            CVulkanContext::Get()->GetGraphicQueue().submit(SubmitInfo
                , CVulkanContext::Get()->GetConvertImageLayoutFence());
        }

        CVulkanContext::Get()->GetDevice().waitForFences(
            {CVulkanContext::Get()->GetConvertImageLayoutFence()}
            , VK_TRUE
            , std::numeric_limits<uint64_t>::max());

        CVulkanContext::Get()->GetDevice().resetFences(
            {CVulkanContext::Get()->GetConvertImageLayoutFence()});

        CVulkanContext::Get()->GetDevice().freeMemory(StagingBufferMemory);
        CVulkanContext::Get()->GetDevice().destroyBuffer(StagingBuffer);



        //创建sampler
        vk::SamplerCreateInfo SamplerCreateInfo;
        SamplerCreateInfo.magFilter=vk::Filter::eLinear;
        SamplerCreateInfo.minFilter=vk::Filter::eLinear;
        SamplerCreateInfo.mipmapMode=vk::SamplerMipmapMode::eLinear;
        SamplerCreateInfo.addressModeU=vk::SamplerAddressMode::eClampToEdge;
        SamplerCreateInfo.addressModeV=vk::SamplerAddressMode::eClampToEdge;
        SamplerCreateInfo.addressModeW=vk::SamplerAddressMode::eClampToEdge;
        SamplerCreateInfo.mipLodBias=0.0f;
        SamplerCreateInfo.anisotropyEnable=VK_FALSE;
        SamplerCreateInfo.maxAnisotropy=1.0f;
        SamplerCreateInfo.compareEnable=VK_FALSE;
        SamplerCreateInfo.compareOp=vk::CompareOp::eAlways;
        SamplerCreateInfo.minLod=0.0f;
        SamplerCreateInfo.maxLod=0.0f;
        SamplerCreateInfo.borderColor=vk::BorderColor::eFloatOpaqueBlack;
        SamplerCreateInfo.unnormalizedCoordinates=VK_FALSE;

        VulkanTextureCube->Sampler=CVulkanContext::Get()->GetDevice().createSampler(SamplerCreateInfo);

    
    }
}

std::shared_ptr<CVulkanTextureCube> CVulkanTextureCubes::FindVulkanObj(TThreadSafeSharedPtr<CWriteInTextureCube> TextureCube)
{
    return TextureCubes[TextureCube];
}
